from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.animation as animation
import random


class BSAnimation():
    # 构造函数，做一些初始化工作（界面、保存状态的 list 等等）
    def __init__(self):
        # 调用父类构造函数
        super().__init__()

        # 初始化一些后续会用到的属性
        self.ani = None
        self.figure = None
        self.canvas = None

        # 初始化 canvas
        self.init_canvas()

        # 初始化记录排序过程的各个步骤 list
        self.steps = []

    # 初始化 canvas
    def init_canvas(self):
        # 1. 创建 matplotlib 的 Figure对象 和 FigureCanvas 对象
        # 创建 Figure
        self.figure = Figure()
        self.figure.clear()
        # 根据 Figure 创建 Canvas
        self.canvas = FigureCanvas(self.figure)

    # 获取 canvas 对象
    def get_canvas(self):
        return self.canvas

    def begin_animation(self):
        # 随机产生一个 list 数据，用来表示待排序集合
        # 生成一个随机整数列表，长度为10，范围在1到100之间（可以根据需求调整）
        random_list = [random.randint(1, 100) for _ in range(5)]
        self.numbers = random_list

        # 5. 根据待排序的数据绘制柱形图
        self.plot_bars(random_list, -1, -1)

        # 开始排序
        self.start_bubble_sort()

    # 绘制初始化的柱状图
    def plot_bars(self, numbers, j, next_j):
        # 1. 清除之前的绘图
        self.figure.clear()
        self.canvas.draw()
        # 在一个包含一个子图的 1x1 网格中添加子图
        ax = self.figure.add_subplot(111)

        # 2. 设置柱形的颜色, 默认所有 bar 都是蓝色，当前正在比较的两个 bar 是红色
        # 根据随机产生的待排序数据的个数，设置所有 bar 默认都是蓝色
        bar_colors = ['b'] * len(numbers)

        # 如果不是初始状态，则将当前比较的两个 bar 设置为 红色
        if j != -1 and next_j != -1:
            bar_colors[j] = 'r'
            bar_colors[next_j] = 'r'

        # 3. 绘制柱形图
        # 参数1：x 坐标；参数2：y坐标；参数3：颜色集合
        ax.bar(range(len(numbers)), numbers, color=bar_colors)

        # 把 canvas 画出来
        self.canvas.draw()

    # 绘制排序完成后的最终状态（将所有 bar 都设置为蓝色
    def plot_final_state(self):
        # 1. 获取最终排序完成后的状态
        final_numbers = self.steps[-1][0]

        # 2. 绘制最终的状态图
        self.plot_bars(final_numbers, -1, -1)

    # 执行冒泡排序, 同时记录状态
    def start_bubble_sort(self):
        # 1. 记录排序过程
        self.record_sorting_steps()

        # 2. 启动动画, 每 500 毫秒更新一次图形
        self.ani = animation.FuncAnimation(self.figure, self.update_plot, interval=500, frames=len(self.steps),
                                           repeat=False, blit=False)

        # 3. 添加动画结束时的事件处理, 绘制最终状态
        self.ani.event_source.add_callback(self.plot_final_state)

        # 4. 绘制第一帧图形
        self.canvas.draw()

    # 参数 frame 表示当前第几帧动画
    def update_plot(self, frame):
        # 1. 获取当前步骤的状态
        numbers, j, next_j = self.steps[frame]

        # 2. 绘制当前步骤的柱形图
        self.plot_bars(numbers, j, next_j)

    # 记录冒泡排序的状态
    # 记录冒泡排序的每一步状态到self.steps列表中。
    def record_sorting_steps(self):
        # 1. 清空之前的记录
        self.steps = []

        # 2. 获取随机产生的待排序数字的个数
        n = len(self.numbers)

        # 3. 开始进行冒泡排序
        for i in range(n):
            for j in range(n - i - 1):
                # 记录当前状态
                self.steps.append((self.numbers[:], j, j + 1))

                # 进行两两比较
                if self.numbers[j] > self.numbers[j + 1]:
                    # 交换元素
                    self.numbers[j], self.numbers[j + 1] = self.numbers[j + 1], self.numbers[j]
                    # 记录交换后的状态
                    self.steps.append((self.numbers[:], j, j + 1))
